/*
 * basestation.cpp
 *
 *  Created on: 2010-1-29
 *      Author: gavinweng
 */

#include <cmath>
#include <set>
#include "basestation.h"
#include "simulator.h"

extern MapBlock *mapblock[SIZE][SIZE];
BaseStation *BS;     // Base Station
int  *OpenBS;        // Base Station state for simulation

RFUnit::RFUnit(int x, int y, double height)
: x(x), y(y), height(height)
{
}

RFUnit::~RFUnit()
{
	userlist.empty();
}

int BaseStation::BSNumber = 0;

BaseStation::BaseStation()
{
}

BaseStation::BaseStation( const RFProperty& bsproperty, int num )
:RFUnit(bsproperty.x, bsproperty.y, bsproperty.height), num(num)
{
	state = OFF;
	userlist.clear();
	capacity = .0;
	for (int i = 0; i < CHANNEL; i++)
	{
		power[i] = bsproperty.power[i];
		bandwidth[i] = bsproperty.bandwidth[i];
		band[i] = 0.0;
		chanOrder[i] = i;
	}
	ChanOrder();
	// Calculate Path Loss Matrix. Shall not be changed during simulation.
	for(int i = 0; i < SIZE; i++)
	{
		for(int j = 0; j < SIZE; j++ )
		{
			pathloss[i][j] = CalculatePathLoss(i, j, x, y);
		}
	}
	Reset();
}

int
BaseStation::GetNumber() const
{
	return num;
}

void
RFUnit::ChanOrder()
{
	for(int i = 0; i < CHANNEL; i++)
	{
		for(int j = i+1; j < CHANNEL; j++)
		{
			if (power[chanOrder[i]] > power[chanOrder[j]])
			{
				int tmp = chanOrder[i];
				chanOrder[i] = chanOrder[j];
				chanOrder[j] = tmp;
			}
		}
	}
}

void
BaseStation::ChanOrder()
{
/*
	// Order channel according to receive power
	for(int i = 0; i < CHANNEL; i++)
	{
		for(int j = i+1; j < CHANNEL; j++)
		{
			if (power[chanOrder[i]] > power[chanOrder[j]])
			{
				int tmp = chanOrder[i];
				chanOrder[i] = chanOrder[j];
				chanOrder[j] = tmp;
			}
		}
	}
*/
	// Order channel according to turn-on order
	for(int i = 0; i < CHANNEL; i++)
	{
		chanOrder[i] = (i + OpenBS[num]) %  CHANNEL;
		power[chanOrder[i]] = POWER - 5 * (CHANNEL - 1 - i);
	}
}

double
RFUnit::GetHeight() const
{
	return height;
}

int
RFUnit::GetX() const
{
	return x;
}

void
RFUnit::SetX(int X)
{
	x = X;
}

int
RFUnit::GetY() const
{
	return y;
}

void
RFUnit::SetY(int Y)
{
	y = Y;
}

double
RFUnit::GetPower(int chan) const
{
	return power[chan];
}

void
RFUnit::SetPower(int chan, double pow)
{
	power[chan] = pow;
}

double
RFUnit::GetBandwidth(int chan) const
{
	return bandwidth[chan];
}

void
RFUnit::SetBandwidth(int chan, double bw)
{
	bandwidth[chan] = bw;
}

double
RFUnit::GetBand(int chan) const
{
	return band[chan];
}

int
RFUnit::GetChanOrder(int order) const
{
	return chanOrder[order];
}

void
RFUnit::SetChanOrder(int order, int chan)
{
	chanOrder[order] = chan;
}

enum RFState
RFUnit::GetState() const
{
	return state;
}

bool
RFUnit::IsOpen() const
{
	return (state == FULL);
}

bool
BaseStation::IsOpen() const
{
	return (state == FULL || state == HALF);
}

int
RFUnit::SetOpen(RFState sta)
{
	int open = 0;
	state = sta;
	UserList_IT it;
    for (it = userlist.begin(); it != userlist.end(); it++)
    {
		if ((*it).cover == true)
		{
			mapblock[ (*it).x ][ (*it).y ]->SetUserCovered( true );
			mapblock[ (*it).x ][ (*it).y ]->SetServeUnit(this);
			mapblock[ (*it).x ][ (*it).y ]->SetServeChannel((*it).channel);
			open += mapblock[ (*it).x ][ (*it).y ]->GetUserNumber();
		}
    }
	return open;
}

void
RFUnit::Reset()
{
	userlist.clear();
	state = OFF;
	ChanOrder();
}

double RFUnit::CalculatePower(int x, int y, int chan) const
{
	if (chan > -1)
	{
		return ( power[chan] - pathloss[x][y] );
	}
	else
	{
		double max = -1000.0;
		for(int i = 0; i < CHANNEL; i++)
		{
			double tmp = power[i] - pathloss[x][y];
			max = ( max > tmp ) ? max : tmp;
		}
		return max;
	}
}

void
RFUnit::AddUser( int X, int Y)
{
	if( mapblock[X][Y]->GetUserCovered() )
	{
		return;
	}
	UserProperty tmpuser(X,Y,
			            mapblock[X][Y]->GetTotalDayTraffic(),
			            mapblock[X][Y]->GetTotalNightTraffic(),
						CalculatePower(X,Y));
	userlist.insert(tmpuser);
}

void
RFUnit::CalculateUserList()
{
	// Add block
	Reset();
	for( int i = 0 ; i < SIZE ; i++ )
	{
		for( int j = 0 ; j < SIZE ; j++ )
		{
			if( CalculatePower(i,j) >= MinPower )
			{
				AddUser( i , j);
			}
		}
	}
}

double
BaseStation::CalculateBandDemand(const UserProperty& user, int chan )
{
	// Calculate SINR and the bandwidth needed correspondingly
	double G = pow( 10 , CalculatePower(user.x,user.y, chan) /10 );
	double interference = 0.0;  // Absolute, not in dB

	for (int i = 0; i < BaseStation::BSNumber; i++)
	{
		if (num != i && OpenBS[i] > -1)
		{
			interference += pow(10, BS[i].CalculatePower(user.x, user.y, chan)/10.0);
		}
	}
	// Apply Shannon's Theorem here: C = BW * log2(1 + SINR)
	// n_0 = -174dBm/Hz
	double SINR = G / ( pow( 10 , -17.4 ) * bandwidth[chan] /10.0 + interference);
	if (SINR < pow (10, GAP / 10.0) )
	{
		return (BANDWIDTH + 1);
	}
	if( Simulator::IsDay() )
	{
		return ( user.dayTrafficDemand / log2( 1 + SINR ) );
	}
	else
	{
		return ( user.nightTrafficDemand / log2( 1 + SINR ) );
	}
}

void
RFUnit::ReCalculateUserList()
{
	// Check if block is covered. If true, delete to avoid double coverage.
	if( userlist.empty() )
		return;
	else
	{
    	UserList_IT it = userlist.begin();
    	while (it != userlist.end())
    	{
    		if ( mapblock[(*it).x][(*it).y]->GetUserCovered() )
    		{
    			if (it!=userlist.begin())
    			{
    				userlist.erase(it--);
    				it++;
    			}
    			else
    			{
    				userlist.erase(it);
    				it = userlist.begin();
    			}
    		}
    		else
    		{
    			it++;
    		}
    	}
	}
}

double
RFUnit::GetCapacity() const
{
	return capacity;
}

void
RFUnit::CalculateCapacity()
{
	// Iterate user list, add user according to power order.
	// Bandwidth is the constraint.
	ReCalculateUserList();
	capacity = 0;
	for(int i = 0; i < CHANNEL; i++)
	{
		band[i] = 0;
	}
	if( userlist.empty() )
	{
		return;
	}
	UserList_IT it = userlist.begin();
	while (it != userlist.end() )
	{
		int  chan = 0; bool flag = false;
		while (!flag && chan < CHANNEL)
		{
			double tmp = CalculateBandDemand(*it, chanOrder[chan]);
			if ( band[chan] + tmp <= bandwidth[chan] )
			{
				band[chan] += tmp;
				if( Simulator::IsDay() )
				{
					capacity += (*it).dayTrafficDemand;
				}
				else
				{
					capacity += (*it).nightTrafficDemand;
				}
				UserProperty tmpUser(*it);
				tmpUser.cover = true; tmpUser.channel = chanOrder[chan];
				if ( it != userlist.begin() )
				{
					userlist.erase(it--);
					userlist.insert(tmpUser);
					it++;
				}
				else
				{
					userlist.erase(it);
					userlist.insert(tmpUser);
					it = userlist.begin();
				}
				flag = true;
			}
			else
			{
				chan++;
			}
		}
   		it++;
	}
}

void
Simulator::CalculateBSState()
{
	int UserServed = 0 , TotalUserNum = 0;
	for(int i = 0 ; i < SIZE ; i++ )
		for(int j = 0 ; j < SIZE ; j++ )
		{
			TotalUserNum += mapblock[i][j]->GetUserNumber();
		}
	int bsnumber = 0;
	double MaxCapacityBSNumber = -1;
	double temp = 0;
	int MaxCapacityBSIndex = -1;

	//DEBUG: not right here!!!
	//while( UserServed < TotalUserNum * RATIO && bsnumber < BaseStation::BSNumber )
	//Need to regenerate list
	int step = SimuStep->value();
	while (bsnumber < step)
	{
		MaxCapacityBSNumber = 0;
		for(int j = 0; j < BaseStation::BSNumber; j++ )
		{
			if ( OpenBS[j] < 0 )
			{
				OpenBS[j] = bsnumber;
				temp = Simulation(bsnumber);
				if( temp > MaxCapacityBSNumber )
				{
					MaxCapacityBSNumber = temp;
					MaxCapacityBSIndex = j;
				}
				OpenBS[j] = -1;
			}
		}
		OpenBS[MaxCapacityBSIndex] = bsnumber;
		double debugwatch = Simulation(bsnumber);
		bsnumber++;
		citymap->RepaintAll();
	}
	for (int i = 0; i < SIZE; i++)
	{
		for(int j = 0; j < SIZE; j++)
		{
			if (mapblock[i][j]->GetUserCovered())
			{
				UserServed += mapblock[i][j]->GetUserNumber();
			}
		}
	}
	CoverRate->setText( QString::number( UserServed / ( TotalUserNum + .0) ) );
	TotalUserNumber->setText( QString::number( TotalUserNum ) );
	ServedUserNumber->setText( QString::number( UserServed ) );
	citymap->RepaintAll();
	StartSimulation->setEnabled( false );
	Reset->setEnabled( true );
	SetTraffic->setEnabled(false);
}

void
Simulator::InitBS()
{
	for (int i = 0; i < BaseStation::BSNumber; i++)
	{
		citymap->RepaintBlock( BS[i].GetX() , BS[i].GetY() );
	}
}

void
Simulator::StartBSSimulation()
{
	CalculateBSState();
}

// Simulate when num BS(s) are open
double
Simulator::Simulation(int num)
{
	for(int i =0; i < BaseStation::BSNumber; i++)
	{
		BS[i].Reset();
	}
	for(int i = 0 ; i < SIZE ; i++ )
	{
		for(int j = 0 ; j < SIZE ; j++ )
		{
			mapblock[i][j]->SetUserCovered( false );
			mapblock[i][j]->SetServeUnit(NULL);
			mapblock[i][j]->SetServeChannel(-1);

			double maxp = MinPower;
			int maxpi = 0;
			for(int k =0; k < BaseStation::BSNumber; k++)
			{
				if ( OpenBS[k] > -1 && BS[k].CalculatePower(i,j) >= maxp )
				{
					maxp = BS[k].CalculatePower(i,j);
					maxpi = k;
				}
			}
			BS[maxpi].AddUser(i,j);
		}
	}
	double capacity = 0.0;
	for(int i = 0; i <= num; i++)
	{
		for(int j = 0; j < BaseStation::BSNumber; j++)
		{
			if(OpenBS[j] == i)
			{
				BS[j].CalculateCapacity();
				capacity += BS[j].GetCapacity();
				BS[j].SetOpen();
			}
		}
	}
	return capacity;

}


void
Simulator::ResetBSSimulation()
{
	for(int i = 0 ; i < SIZE ; i++ )
	{
		for(int j = 0 ; j < SIZE ; j++ )
		{
			mapblock[i][j]->SetUserCovered( false );
			mapblock[i][j]->SetServeUnit(NULL);
			mapblock[i][j]->SetServeChannel(-1);
		}
	}
	for(int i = 0 ; i < BaseStation::BSNumber ; i++ )
	{
		BS[i].Reset();
		OpenBS[i] = -1;
	}
	Reset->setEnabled( false );
	StartSimulation->setEnabled( true );
	SetTraffic->setEnabled( true );
	citymap->RepaintAll();
}

// Input BS sites.
void
Simulator::InputBSSites()
{
	std::ifstream fin( "BaseStationa.txt", std::ios_base::in );
	RFProperty bsproperty;
	if( !fin )
	{
		// Default BS sites setting.

		BaseStation::BSNumber = BSNUMBER;
		citymap = new CityMap();

		OpenBS = new int[BaseStation::BSNumber];
		BS = new BaseStation[BaseStation::BSNumber];
		for(int i = 0 ; i < BaseStation::BSNumber ; i++ )
		{
			bsproperty.x = 10 + SIZE / 5 * ( i % 5 );
			bsproperty.y = 10 + SIZE / 5 * ( i / 5 );
			bsproperty.height = 12.5;
			for (int j = 0; j < CHANNEL; j++ )
			{
				bsproperty.power[j] = POWER;// + 10 * (i % CHANNEL);
				bsproperty.bandwidth[j] = BANDWIDTH / (CHANNEL + .0);
			}
			mapblock[bsproperty.x ][bsproperty.y ]->SetBSExist(true);
			BS[i] = BaseStation(bsproperty, i);
			OpenBS[i] = -1;
		}
	}
	else
	{
		char ch;
		fin>>BaseStation::BSNumber;
		citymap = new CityMap();

		OpenBS = new int[BaseStation::BSNumber];
		BS = new BaseStation[BaseStation::BSNumber];
		if( fin.good() )
		{
			fin.get( ch );
			while(ch != '\n' && fin.good() )
				fin.get( ch );
		}
		for(int i = 0 ; i < BaseStation::BSNumber ; i++ )
		{
				fin>>bsproperty.x;
				fin.get( ch );
				fin>>bsproperty.y;
				fin.get( ch );
				fin>>bsproperty.height;
				fin.get( ch );
				for (int j = 0; j < CHANNEL; j++)
				{
					fin>>bsproperty.power[j];
					fin.get( ch );
				}
				for (int j = 0; j < CHANNEL; j++)
				{
					fin>>bsproperty.bandwidth[j];
					fin.get( ch );
				}
				if( i < BaseStation::BSNumber - 1 )
				{
					fin.get( ch );
					while(ch != '\n' )
						fin.get( ch );
				}
				BS[i] = BaseStation(bsproperty, i);
				OpenBS[i] = -1;
		}
	}
	InitBS();
}

